package com.ad.gateway.interceptor;

import com.ad.gateway.config.IgnoreUrlsConfig;
import com.ad.payload.exception.ErrorMapping;
import com.ad.payload.exception.ExceptionEntity;
import com.ad.payload.util.JwtUtil;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.omg.CORBA.SystemException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.List;
import java.util.UUID;

/**
 * @author:dxgong
 * @日期 2021/3/21 00:16
 * @注释 jwt请求鉴权
 */
@Component
@Slf4j
public class AuthenticationInterceptor implements GlobalFilter, Ordered {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String authentication = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);

        URI uri = request.getURI();
        PathMatcher pathMatcher = new AntPathMatcher();
        // url白名单跳过jwt校验
        List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
        for (String ignoreUrl : ignoreUrls) {
            if (pathMatcher.match(ignoreUrl, uri.getPath())) {
                return chain.filter(exchange);
            }
        }


        Claims jwt = null;
        boolean expired = true;

        try {
            jwt = jwtUtil.getJwtClaimsFromToken(authentication);
            // expired = jwt.getExpiration().getTime() < System.currentTimeMillis();
        }catch (Exception e) {
            e.printStackTrace();
            return authFail(exchange);
        }

        //token过期
//        if(!expired){
//            return tokenExpired(exchange);
//        }

        ServerHttpRequest.Builder builder = request.mutate();
        if (hasUserPermission(uri.getPath(), jwt)) {
            // 有权限继续执行,同时把token里面的用户ID，企业ID放入到请求头部，给内部服务使用
            builder.header("Authorization", authentication);
            return chain.filter(exchange.mutate().request(builder.build()).build());
        } else {
            // 没有权限返回错误
            return permissionDenied(exchange);
        }
    }

    /**
     * 判断用户权限
     *
     * @return boolean true 放行, false: 不放行
     */
    private Boolean hasUserPermission(String url, Claims jwt) {
        /**
         * ...这里还需判断用户权限的接口业务
         */
        return true;
    }

    private Mono<Void> notLogin(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders httpHeaders = response.getHeaders();
        httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
        httpHeaders.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        String warningStr = getExceptionMessage(ErrorMapping.NO_LOGIN);
        DataBuffer bodyDataBuffer = response.bufferFactory().wrap(warningStr.getBytes());
        return response.writeWith(Mono.just(bodyDataBuffer));
    }

    private Mono<Void> permissionDenied(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders httpHeaders = response.getHeaders();
        httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
        httpHeaders.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        String warningStr = getExceptionMessage(ErrorMapping.PERMISSION_DENIED);
        DataBuffer bodyDataBuffer = response.bufferFactory().wrap(warningStr.getBytes());
        return response.writeWith(Mono.just(bodyDataBuffer));
    }

    private Mono<Void> authFail(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders httpHeaders = response.getHeaders();
        httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
        httpHeaders.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        String warningStr = getExceptionMessage(ErrorMapping.ACCESS_TOKEN_INVALID);
        DataBuffer bodyDataBuffer = response.bufferFactory().wrap(warningStr.getBytes());
        return response.writeWith(Mono.just(bodyDataBuffer));
    }

    @Override
    public int getOrder() {
        return 0;
    }


    private String getHeaderKey(){
        String  uuid = UUID.randomUUID().toString();
        return uuid;
    }

    private Mono<Void> tokenExpired(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders httpHeaders = response.getHeaders();
        httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
        httpHeaders.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        String warningStr = getExceptionMessage(ErrorMapping.ACCESS_TOKEN_EXPIRED);
        DataBuffer bodyDataBuffer = response.bufferFactory().wrap(warningStr.getBytes());
        return response.writeWith(Mono.just(bodyDataBuffer));
    }


    private String getExceptionMessage(int errorCode){
        ExceptionEntity entity = new ExceptionEntity();
        entity.setExceptionType(SystemException.class.getName());
        entity.setErrCode(errorCode);
        entity.setReferenceCode(ErrorMapping.getReferenceCode());
        entity.setMessage(ErrorMapping.MESSAGE_MAP.get(errorCode));
        return JSON.toJSONString(entity);
    }
}
